using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using OhioState.AssetManaged;
using OhioState.Math;

namespace OhioState.Graphics.Proxy
{
    using Vertex = Vector3;

    /// <summary>
    /// AxesPlane class that creates 3 mutually orthogonal, axis aligned planes
    /// </summary>
    [Serializable()]
    public class AxesPlaneProxy :SharedAsset, IDrawable
    {
        private IShape _iShape;
        //private Plane _plane;
        private Vector3 _point;
        
        /// <summary>
        /// AxesPlane constructor
        /// </summary>
        /// <param name="point"></param>
        /// <param name="iShape"></param>
        public AxesPlaneProxy(Vector3 point, IShape iShape)
            : base("AxesPlane")
        {
            _iShape = iShape;
            _point = point;
            _material = MaterialProxy.White;
            _boxVertices = new BoxVertex[8];
            _edgeList = new ActiveEdge[12];
            _currentSlice = new List<Vector3>();
            _xySlice = new List<Vector3>();
            _xzSlice = new List<Vector3>();
            _yzSlice = new List<Vector3>();
            InitSlicingBox();
           
        }
        private BoxVertex[] _boxVertices;
        private ActiveEdge[] _edgeList;
        //private Vector3[] _currentSlice;
        private List<Vector3> _currentSlice;
        private List<Vector3> _xySlice;
        private List<Vector3> _xzSlice;
        private List<Vector3> _yzSlice;


        private int numIntersection = 0;
        /// <summary>
        /// Initializes the box with vertices and edgelist information 
        /// </summary>
        private void InitSlicingBox()
        {
            SharedBox sBox = _iShape as SharedBox;
            for (int i = 0; i < _boxVertices.Length; i++)
                _boxVertices[i] = new BoxVertex();

            for (int i = 0; i < _edgeList.Length; i++)
                _edgeList[i] = new ActiveEdge();

            Vertex v0 = new Vertex(sBox.BBox.LowerLeft);
            Vertex v1 = new Vertex();
            Vertex v2 = new Vertex();
            Vertex v3 = new Vertex();
            Vertex v4 = new Vertex();
            Vertex v5 = new Vertex();
            Vertex v6 = new Vertex(sBox.BBox.UpperRight);
            Vertex v7 = new Vertex();

            //
            // Note BoundingBox is defined in world space, BoundingBox.LowerLeft is the origin of ROI space,
            // and BoundingBox.UpperRight is the farthest vertex on the box
            //

            //
            // Set the other 6 vertices on the box in ROI space
            //

            v1[0] = v6[0]; v1[1] = v0[1]; v1[2] = v0[2];
            v2[0] = v6[0]; v2[1] = v6[1]; v2[2] = v0[2];
            v3[0] = v0[0]; v3[1] = v6[1]; v3[2] = v0[2];
            v4[0] = v0[0]; v4[1] = v0[1]; v4[2] = v6[2];
            v5[0] = v6[0]; v5[1] = v0[1]; v5[2] = v6[2];
            v7[0] = v0[0]; v7[1] = v6[1]; v7[2] = v6[2];
            
            _boxVertices[0].ID = 0;
            _boxVertices[0].Pos = v0;

            _boxVertices[1].ID = 1;
            _boxVertices[1].Pos = v1;

            _boxVertices[2].ID = 2;
            _boxVertices[2].Pos = v2;

            _boxVertices[3].ID = 3;
            _boxVertices[3].Pos = v3;

            _boxVertices[4].ID = 4;
            _boxVertices[4].Pos = v4;

            _boxVertices[5].ID = 5;
            _boxVertices[5].Pos = v5;

            _boxVertices[6].ID = 6;
            _boxVertices[6].Pos = v6;

            _boxVertices[7].ID = 7;
            _boxVertices[7].Pos = v7;

            _edgeList[0].Vertex_id[0] = _boxVertices[0].ID;
            _edgeList[0].Vertex_id[1] = _boxVertices[1].ID;

            _edgeList[1].Vertex_id[0] = _boxVertices[1].ID;
            _edgeList[1].Vertex_id[1] = _boxVertices[2].ID;

            _edgeList[2].Vertex_id[0] = _boxVertices[2].ID;
            _edgeList[2].Vertex_id[1] = _boxVertices[3].ID;

            _edgeList[3].Vertex_id[0] = _boxVertices[1].ID;
            _edgeList[3].Vertex_id[1] = _boxVertices[3].ID;

            _edgeList[4].Vertex_id[0] = _boxVertices[4].ID;
            _edgeList[4].Vertex_id[1] = _boxVertices[5].ID;

            _edgeList[5].Vertex_id[0] = _boxVertices[5].ID;
            _edgeList[5].Vertex_id[1] = _boxVertices[6].ID;

            _edgeList[6].Vertex_id[0] = _boxVertices[6].ID;
            _edgeList[6].Vertex_id[1] = _boxVertices[7].ID;

            _edgeList[7].Vertex_id[0] = _boxVertices[4].ID;
            _edgeList[7].Vertex_id[1] = _boxVertices[7].ID;

            _edgeList[8].Vertex_id[0] = _boxVertices[0].ID;
            _edgeList[8].Vertex_id[1] = _boxVertices[4].ID;

            _edgeList[9].Vertex_id[0] = _boxVertices[1].ID;
            _edgeList[9].Vertex_id[1] = _boxVertices[5].ID;

            _edgeList[10].Vertex_id[0] = _boxVertices[2].ID;
            _edgeList[10].Vertex_id[1] = _boxVertices[6].ID;

            _edgeList[11].Vertex_id[0] = _boxVertices[3].ID;
            _edgeList[11].Vertex_id[1] = _boxVertices[7].ID;

        }
        /// <summary>
        /// Finds intersection points of axis planes with box
        /// </summary>
        public void FindIntersections()
        {
            
            _xySlice.Clear();
            _xzSlice.Clear();
            _yzSlice.Clear();

            //Hack. For now box only 
            SharedBox sBox = IShape as SharedBox;
            float originX, originY, originZ;
            originX = sBox.BBox.LowerLeft[0];
            originY = sBox.BBox.LowerLeft[1];
            originZ = sBox.BBox.LowerLeft[2];

            _xySlice.Add(new Vector3(originX + 0.0f * sBox.BBox.Width, originY + 0.0f * sBox.BBox.Height, originZ + _point[2] * sBox.BBox.Depth));
            _xySlice.Add(new Vector3(originX + 1.0f * sBox.BBox.Width, originY + 0.0f * sBox.BBox.Height, originZ + _point[2] * sBox.BBox.Depth));
            _xySlice.Add(new Vector3(originX + 1.0f * sBox.BBox.Width, originY + 1.0f * sBox.BBox.Height, originZ + _point[2] * sBox.BBox.Depth));
            _xySlice.Add(new Vector3(originX + 0.0f * sBox.BBox.Width, originY + 1.0f * sBox.BBox.Height, originZ + _point[2] * sBox.BBox.Depth));

            // Find intersection for XZ plane

            _xzSlice.Add(new Vector3(originX + 0.0f * sBox.BBox.Width, originY + _point[1] * sBox.BBox.Height, originZ + 1.0f * sBox.BBox.Depth));
            _xzSlice.Add(new Vector3(originX + 1.0f * sBox.BBox.Width, originY + _point[1] * sBox.BBox.Height, originZ + 1.0f * sBox.BBox.Depth));
            _xzSlice.Add(new Vector3(originX + 1.0f * sBox.BBox.Width, originY + _point[1] * sBox.BBox.Height, originZ + 0.0f * sBox.BBox.Depth));
            _xzSlice.Add(new Vector3(originX + 0.0f * sBox.BBox.Width, originY + _point[1] * sBox.BBox.Height, originZ + 0.0f * sBox.BBox.Depth));

            // Find intersection for YZ plane

            _yzSlice.Add(new Vector3(originX + _point[0] * sBox.BBox.Width, originY + 0.0f * sBox.BBox.Height, originZ + 0.0f * sBox.BBox.Depth));
            _yzSlice.Add(new Vector3(originX + _point[0] * sBox.BBox.Width, originY + 1.0f * sBox.BBox.Height, originZ + 0.0f * sBox.BBox.Depth));
            _yzSlice.Add(new Vector3(originX + _point[0] * sBox.BBox.Width, originY + 1.0f * sBox.BBox.Height, originZ + 1.0f * sBox.BBox.Depth));
            _yzSlice.Add(new Vector3(originX + _point[0] * sBox.BBox.Width, originY + 0.0f * sBox.BBox.Height, originZ + 1.0f * sBox.BBox.Depth));

            Vector3 norm = new Vector3(0.0f, 0.0f, 1.0f);
            SortIntersections(_xySlice, norm);
            norm[0] = 0.0f; norm[1] = 1.0f; norm[2] = 0.0f;
            SortIntersections(_xzSlice, norm);
            norm[0] = 1.0f; norm[1] = 0.0f; norm[2] = 0.0f;
            SortIntersections(_yzSlice, norm);
        }
        
      
        private void SortIntersections(List<Vector3> slices, Vector3 norm)
        {
            Vector3 meanXpt = new Vector3();
            Vector3 sum = new Vector3();
            for (int i = 0; i < 4; i++)
            {
                sum += slices[i];
            }
            meanXpt = sum / slices.Count;
            if (slices.Count <= 3)
                return;

            //Find the longest component of the normal and use the corresponding plane

            float x, y, z;
            x = norm[0];
            y = norm[1];
            z = norm[2];
            for (int i = 0; i < 3; i++)
            {
                if (z >= x && z >= y)
                {
                    double ai = System.Math.Atan2(slices[i][1] - meanXpt[1], slices[i][0] - meanXpt[0]);
                    if (ai < 0) ai += 2 * System.Math.PI;
                    for (int k = i + 1; k < numIntersection; k++)
                    {
                        double ak = System.Math.Atan2(slices[k][1] - meanXpt[1], slices[k][0] - meanXpt[0]);
                        if (ak < 0) ak += 2 * System.Math.PI;
                        if (ak < ai)
                        {
                            Vector3 temp = slices[i];
                            slices[i] = slices[k];
                            slices[k] = temp;
                            ai = ak;
                        }
                    }
                }
                else if (y >= x && y >= z)
                {
                    double ai = System.Math.Atan2(slices[i][0] - meanXpt[0], slices[i][2] - meanXpt[2]);
                    if (ai < 0) ai += 2 * System.Math.PI;
                    for (int k = i + 1; k < numIntersection; k++)
                    {
                        double ak = System.Math.Atan2(slices[k][0] - meanXpt[0], slices[k][2] - meanXpt[2]);
                        if (ak < 0) ak += 2 * System.Math.PI;
                        if (ak < ai)
                        {
                            Vector3 temp = slices[i];
                            slices[i] = slices[k];
                            slices[k] = temp;
                            ai = ak;
                        }
                    }

                }
                else if (x >= y && x >= z)
                {
                    double ai = System.Math.Atan2(slices[i][2] - meanXpt[2], slices[i][1] - meanXpt[1]);
                    if (ai < 0) ai += 2 * System.Math.PI;
                    for (int k = i + 1; k < numIntersection; k++)
                    {
                        double ak = System.Math.Atan2(slices[k][2] - meanXpt[2], slices[k][1] - meanXpt[1]);
                        if (ak < 0) ak += 2 * System.Math.PI;
                        if (ak < ai)
                        {
                            Vector3 temp = slices[i];
                            slices[i] = slices[k];
                            slices[k] = temp;
                            ai = ak;
                        }
                    }

                }

            }

        }

        #region IDrawable interface
        /// <summary>
        /// Implements the IDrawable BoundingVolume method
        /// </summary>
        public IBoundingVolume BoundingVolume
        {
            get { return ((SharedBox)_iShape).BBox; }
        }

        private OSUColor _color = new OSUColor(OSUColor.White);
        /// <summary>
        /// Gets or sets the color for axesplane
        /// </summary>
        public OSUColor Color
        {
            get { return new OSUColor(_color); }
            set
            {
                _color = new OSUColor(value);
                FireUpdateChangedEvent(this);
            }
        }
        private MaterialProxy _material;
        /// <summary>
        /// Gets or sets the material for axesplane 
        /// </summary>
        public IMaterial Material
        {
            get { return _material; }
            set
            {
                //
                // For fields that contain shared objects, follow this sequence:
                //   1. rewire the notification for the shared object
                //        this property points to.
                //   2. set the property.
                //   3. perform any internal updates.
                //   4. Implicit in step 3 would be changing this 
                //         object and hence notifying it's subscribers.
                //
                IAssetManagedHandler changedEvent = new IAssetManagedHandler(SPlane_ChangedEvent);
                MaterialProxy mat = value as MaterialProxy;
                if (mat != null)
                {
                    this.SetPropertyEventHandling(_material, mat, changedEvent);
                    _material = mat;
                    SPlane_ChangedEvent(mat);
                }
                _material.Compositor.DepthTest = DepthMode.ON;
            }
        }
        /// <summary>
        /// Implementation of the IDrawable Render method
        /// </summary>
        /// <param name="panel"> renderable panel interface </param>
        public void Render(IRenderPanel panel)
        {
        }
        #endregion IDrawable interface

        private void SPlane_ChangedEvent(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }
        /// <summary>
        /// Overrides shared object ToString method
        /// </summary>
        /// <returns>A string representing parent shared object name</returns>
        public override String ToString()
        {
            return base.Name;
        }


        # region Public Properties

        //public Plane Plane
        //{
        //    get { return _plane; }
        //    set 
        //    {
        //        IAssetManagedHandler changedEvent = new IAssetManagedHandler(Plane_ChangedEvent);
        //        this.SetPropertyEventHandling(_plane, value, changedEvent);
        //        _plane = value;
        //        Plane_ChangedEvent(_plane);
        //    }
        //}
        /// <summary>
        /// Gets or sets the Point through which the axesplane passes through 
        /// </summary>
        public Vector3 Point
        {
            get { return _point; }
            set
            {
                _point = value;
                FireUpdateChangedEvent(this);
            }
        }       
        /// <summary>
        /// Gets or sets the IShape for which the axesplane is defined 
        /// </summary>
        public IShape IShape
        {
            get { return _iShape; }
            set 
            {
                IAssetManagedHandler changedEvent = new IAssetManagedHandler(Shape_ChangedEvent);
                this.SetPropertyEventHandling(_iShape, value, changedEvent);
                _iShape = value;
                Shape_ChangedEvent(_iShape);
            }
        }
        /// <summary>
        /// Gets the xySlice
        /// </summary>
        public List<Vector3> XYSlice
        {
            get { return _xySlice; }
        }
        /// <summary>
        /// Gets the xzSlice
        /// </summary>
        public List<Vector3> XZSlice
        {
            get { return _xzSlice; }
        }
        /// <summary>
        /// Gets the yzSlice
        /// </summary>
        public List<Vector3> YZSlice
        {
            get { return _yzSlice; }
        }
        /// <summary>
        /// Gets the shapetype of the IShape
        /// </summary>
        public ShapeType ShapeType
        {
            get { return ShapeType; }
            //set { ShapeType = value; }
        }

        #endregion
        private void AxesPlane_ChangedEvent(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }
        ///// <summary>
        ///// An event handler for Plane changed event
        ///// </summary>
        ///// <param name="obj">The Plane object</param>
        //protected void Plane_ChangedEvent(IAssetManaged obj)
        //{
        //    FireUpdateChangedEvent(this);
        //}
        /// <summary>
        /// An event handler for shape changed event
        /// </summary>
        /// <param name="obj">Shape object</param>
        protected void Shape_ChangedEvent(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }
        /// <summary>
        /// Overrides the base class's RequireAssets method
        /// </summary>
        public override void ReaquireAssets()
        {
            //
            // Use the Properties to ensure the old event handlers are removed and new
            //    ones are added. Particularly if we are setting an item to itself.
            //
            //IAssetManaged item = AssetManager.Instance.GetItem(_plane.GetType(), _plane.Name);
            //if (item != _plane)
            //{
            //    this.Plane = item as Plane;
            //}
            IAssetManaged item = AssetManager.Instance.Find(_material.GetType(), _material.Name);
            if (item != _material)
            {
                this.Material = item as IMaterial;
            }
            item = AssetManager.Instance.Find(_iShape.GetType(), _iShape.Name);
            if (item != _iShape)
            {
                this.IShape = item as IShape;
            }
        }
        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            IAssetManagedHandler changedEvent = new IAssetManagedHandler(AxesPlane_ChangedEvent);
            //_plane.AddChangedEventHandler(changedEvent);
            _material.AddChangedEventHandler(changedEvent);
            IAssetManaged obj = this._iShape as IAssetManaged;
            if (obj != null)
            {
                obj.AddChangedEventHandler(changedEvent);
            }
        }
    }
    
}
